/**
 * Created by Administrator on 2017/1/16.
 */

var GamePlayer = require('../../game/player').GamePlayer;
var HandCards = require('./handCards').HandCards;
var MJ = require('./MJ');

exports.MJPlayer = GamePlayer.extend({
    _handCards: null,       // 手牌
    _initColors: null,      // 起手花色
    _selectColor: -1,        // 定缺的花色,-1表示未定缺

    _playCards: [],         // 打出去的牌{card, task}
    _pengCards: [],         // 碰了的牌，card
    _gangCards: [],         // 杠了的牌，正常杠{card, from, gang, targets:[], overdue, unscored}，特殊杠{type:0, cards:[], buCards:[], buCard:0, overdue, unscored}
    _gangTasks: [],
    _chiCards: [],          // 吃了的牌，{card, from, selectCard}

    _lastGang: null,        // 杠牌信息

    _pengCardsFrom: {},    // 碰牌的来源, card -> playerIndex

    _ting: {},               // 听牌状态
    _isBaoTing: false,     // 是否报听
    _isPassHu: false,      // 是否有跳过胡牌
    _passHuTimes: 0,       // 跳过胡牌的次数
    _baoTingPassHuTimes:0,// 报听后跳过胡牌的次数
    _tingCards: [],        // 可以听的牌{card, pattern}

    //-------------------------------------------------------------------------
    _prevGang:[],         // 用于计算杠上杠

    //-------------------------------------------------------------------------

    init: function(uid, msg) {
        this._super(uid, msg);

        this._handCards = new HandCards();
        this._initColors = [];
        this._selectColor = -1;

        this._stat.roundResult = 0;         // 本轮结果
        this._stat.roundTarget = [];        // 本轮管住的人坐位列表，将向它们收取（牌型分+其它分）
        this._stat.roundPattern = 0;        // 本轮牌型，胡牌者为胡牌的牌型，未胡者为最大听牌牌型
        this._stat.roundPatternScore = 0;  // 本轮牌型分
        this._stat.roundGangScore = 0;      // 本轮杠分
        this._stat.roundItemScores = {};    // 本轮其它分
        this._stat.roundScoreRules = {};    // 本轮计分规则(用于标注发生了什么特殊规则)
        this._stat.roundScoreDetails = []; // 本局积分详情{reason:0, score:0}
        this._stat.huCard = 0;               // 胡的牌

        this._playCards = [];
        this._pengCards = [];
        this._gangCards = [];
        this._chiCards = [];

        this._lastGang = null;

        this._pengCardsFrom = {};

        this._isBaoTing = false;
        this._isPassHu = false;
        this._passHuTimes = 0;
        this._baoTingPassHuTimes = 0;
        this._tingCards = [];

        this._prevGang = [];
    },

    isDealer: function() {
        return this.getIndex() == this.getRoom().getDealerIndex();
    },

    resetPrevGang: function() {
        this._prevGang = [];
    },

    pushPregGang: function(gang) {
        this._prevGang.push(gang);
    },

    /**
     * 初始化发牌
     * @param cardArr
     */
    initHandCards: function(cardArr) {
        this._handCards.init(cardArr);

        this.sendReplay("onInitHandCards", {
            playerIndex: this.getIndex(),
            cards: clone(this._handCards.getCards()),
            remain: this.getRoom().getPublicCards().getRemainNum()
        });

        var colors = {};
        ExObject.eachKeyNum(this._handCards.getCards(), function(card, num) {
            colors[MJ.getColor(card)] = 1;
        });
        this._initColors = Object.keys(colors);
    },

    setSelectColor: function(color) {
        this._selectColor = color;
    },

    getSelectColor: function() {
        return this._selectColor;
    },

    isSelectColor: function() {
        return this._selectColor != -1;
    },

    /**
     * 摸一张牌
     * @param card
     */
    addNewCard: function(card) {
        this._handCards.addNewCard(card);
        logger.debug("room=%d index=%d addNewCard card=%d cards=%j", this.getRoom().rid(), this.getIndex(), card, this._handCards.getCards());
    },

    /**
     * 上次摸的牌
     * @returns {number}
     */
    getLastCard: function() {
        return this._handCards.getLastNewCard();
    },

    /**
     * 起手牌的最后一张牌
     * @returns {*|number}
     */
    getInitLastCard: function() {
        return this._handCards.getInitLastCard();
    },

    /**
     * 起手牌的花色
     * @returns {null}
     */
    getInitColors: function() {
        return this._initColors;
    },

    /**
     * 是否是第一手，用于判断天胡地胡
     * @returns {boolean}
     */
    isFirstHand: function() {
        return this.getPlayHand() == 0;
    },

    /**
     * 是第几手牌
     */
    getPlayHand: function() {
        return this._playCards.length +
            this._pengCards.length +
            this._gangCards.length +
            this._chiCards.length;
    },

    getPlayCards: function() {
        var cards = [];
        this._playCards.forEach(function(cardInfo) {
            if (!cardInfo.task) {
                cards.push(cardInfo.card);
            }
        });
        return cards;
    },

    /**
     * 所有打出去牌的数量，包括被别人碰/杠/胡的牌
     */
    getPlayCardCount: function() {
        return this._playCards.length;
    },

    /**
     * 出牌
     * @param card
     * @returns {boolean}
     */
    playCard: function(card) {
        if (!this._handCards.play(card)) {
            logger.error("room=%d index=%d playCard card=%d failed", this.getRoom().rid(), this.getIndex(), card);
            return false;
        }

        this._playCards.push({'card':card});
        logger.debug("room=%d index=%d playCard card=%d cards=%j", this.getRoom().rid(), this.getIndex(), card, this._handCards.getCards());
        return true;
    },

    /**
     * 上次出的牌
     * @returns {number}
     */
    getLastPlayCard: function() {
        var playCount = this._playCards.length;
        return playCount > 0 ? this._playCards[playCount - 1].card : 0;
    },

    /**
     * 设置上次出牌的事件，被碰/杠/胡
     * @param task
     */
    setLastPlayCardTask: function(task) {
        if (this._playCards.length > 0) {
            this._playCards[this._playCards.length - 1].task = task;
        }
    },

    /**
     * 检查是否可以碰牌
     * @param card
     * @returns {boolean}
     */
    checkPengWithCard: function(card) {
        if (this.getRoom().isSelectColor()) {
            if (MJ.getColor(card) == this.getSelectColor()) {
                return false;
            }
        }

        return this._handCards.getCardNum(card) >= 2;
    },

    /**
     * 碰牌
     * @param task 碰牌事件
     * @returns {boolean}
     */
    pengCard: function(task) {
        var card = task.card;
        if (!this._handCards.peng(card)) {
            logger.error("room=%d index=%d pengCard task=%d failed", this.getRoom().rid(), this.getIndex(), task);
            return false;
        }

        this._pengCards.push(card);
        this._pengCardsFrom[card] = task.from;
        logger.debug("room=%d index=%d pengCard task=%j cards=%j", this.getRoom().rid(), this.getIndex(), task, this._handCards.getCards());
        return true;
    },

    existPengCard: function(card) {
        return this._pengCards.indexOf(card) != -1;
    },

    getPengCardFrom: function(card) {
        return this._pengCardsFrom[card];
    },

    /**
     * 检查是否可以吃牌
     * @param card
     */
    checkChiWithCard: function(card) {
        if (!this.checkChiCondition(card)) {
            return null;
        }

        var color = MJ.getColor(card);
        var point = MJ.getPoint(card);
        var maxPoint = MJ.ColorCardNum[color].point;

        var maxCards = [];
        for (var p = 0; p < 3; ++p) {
            var tmpPoint = point + p;
            if (tmpPoint >= 3 && tmpPoint <= maxPoint) {
                var exist = true;
                for (var i = 0; i < 3; ++i) {
                    var tmpCard = MJ.getValue(color, tmpPoint - i);
                    if (tmpCard != card && this._handCards.getCardNum(tmpCard) < 1) {
                        exist = false; break;
                    }
                }

                if (exist) {
                    maxCards.push(MJ.getValue(color, tmpPoint));
                }
            }
        }

        return maxCards.length > 0 ? maxCards : null;
    },

    chiCard: function(chiTask) {
        var card = chiTask.card;
        var selectCard = chiTask.selectCard;
        if (!selectCard || (chiTask.maxCards.indexOf(selectCard) == -1)) {
            selectCard = chiTask.maxCards[0];
            chiTask.selectCard = selectCard;
        }

        if (MJ.getPoint(selectCard) < 3) {
            return false;
        }

        var exist = true;
        for (var i = 0; i < 3; ++i) {
            var tmpCard = selectCard - i;
            if (tmpCard != card && this._handCards.getCardNum(tmpCard) < 1) {
                exist = false; break;
            }
        }

        if (!exist) {
            return false;
        }

        for (var i = 0; i < 3; ++i) {
            var tmpCard = selectCard - i;
            if (tmpCard != card) {
                this._handCards.delCard(tmpCard);
            }
        }

        var chiInfo = clone(chiTask);
        delete chiInfo.maxCards;
        this._chiCards.push(chiInfo);

        return true;
    },

    /**
     * 检查杠
     * @returns []
     */
    checkGang: function() {
        var room = this.getRoom();
        if (room.getPublicCards().isEmpty()) {    // 公牌已经没有了
            return [];
        }

        var gangArray = [];

        var lastCard = this._handCards.getLastNewCard();
        if (this.getIndex() != room.getCurPlay() || room.getCurTask()) {
            lastCard = 0;
        }

        if (lastCard) { //优先杠刚摸上的牌
            if (this._handCards.getCardNum(lastCard) == 4) {
                gangArray.push({card:lastCard, gang:MJ.Gang.AN});
            }

            if (this._pengCards.indexOf(lastCard) != -1) {
                gangArray.push({card:lastCard, gang:MJ.Gang.BA});
            }
        }

        var anGangCards = this._handCards.getAnGangCards();
        for (var i = 0; i < anGangCards.length; ++i) {
            if (anGangCards[i] != lastCard) {
                var gangInfo = {card:anGangCards[i], gang:MJ.Gang.ANS};
                if (this.getRoom().getHideOption("anGangOverdue")) {
                    if (this.getPlayHand() != this.getRoom().getCurGang()) {
                        gangInfo.overdue = true;
                    }
                }
                gangArray.push(gangInfo);
            }
        }

        this._pengCards.forEach(function(card) {
            if (this._handCards.getCardNum(card) > 0) {
                if (card != lastCard) {
                    gangArray.push({card: card, gang:MJ.Gang.BA, overdue: true});
                }
            }
        }.bind(this));

        var from = this.getIndex();
        gangArray.forEach(function(gangInfo) {
           gangInfo.from = from;
        });

        return gangArray;
    },

    /**
     * 检查点杠
     * @param card
     * @returns {boolean}
     */
    checkGangWithCard: function(card) {
        if (this.getRoom().isSelectColor()) {
            if (MJ.getColor(card) == this.getSelectColor()) {
                return false;
            }
        }

        if (this.getRoom().getPublicCards().isEmpty()) {    // 公牌已经没有了
            return false;
        }

        return (this._handCards.getCardNum(card) == 3);
    },

    /**
     * 杠牌
     * @param gangTask {} 杠牌事件
     * @returns {*} 杠牌信息
     */
    gangCard: function(gangTask) {
        if (gangTask.gangArray) {
            var selectGang = gangTask.selectGang || 0;
            if (selectGang > gangTask.gangArray.length) {
                selectGang = 0;
            }

            gangTask = gangTask.gangArray[selectGang];
        }

        var from = gangTask.from;
        var card = gangTask.card;
        var gang = gangTask.gang;

        var playerIndex = this.getIndex();
        var targets = [];

        switch (gang) {
            case MJ.Gang.BA:
                var indexOfPeng = this._pengCards.indexOf(card);
                if (indexOfPeng != -1) {
                    this._handCards.delCard(card);
                    this._pengCards.splice(indexOfPeng, 1);
                }
                break;
            case MJ.Gang.DIAN:
                this._handCards.dianGang(card);
                var fromPlayer = this.getRoom().getPlayerByIndex(from);
                if (fromPlayer) {
                    fromPlayer.setLastPlayCardTask(MJ.Task.GANG);
                }
                break;
            case MJ.Gang.AN:
            case MJ.Gang.ANS:
                this._handCards.anGang(card);
                break;
            default:
                break;
        }

        if (gang == MJ.Gang.DIAN) {
            targets.push(from);
        } else {
            this.getRoom().enumPlayers(function(eIndex, ePlayer) {
                if (eIndex != playerIndex && !ePlayer.isRoundHu()) {
                    targets.push(eIndex);
                }
            });
        }

        var gangInfo = clone(gangTask);
        delete gangInfo.playerIndex;
        gangInfo.targets = targets;

        // 杠上杠信息
        //if (this._prevGang.length > 0) {
        //    gangInfo.prevGang = clone(this._prevGang);
        //}

        var fromPlayer = this.getRoom().getPlayerByIndex(from);
        if (fromPlayer.getLastGangG()) {
            gangInfo.prevGang = fromPlayer.getLastGangG();
        }

        this._gangCards.push(gangInfo);
        this._lastGang = gangInfo;

        logger.debug("room=%d index=%d gangCard card=%d cards=%j", this.getRoom().rid(), this.getIndex(), card, this._handCards.getCards());

        return gangInfo;
    },

    /**
     * 取消最后一张杠牌
     * @returns {*}
     */
    cancelLastGang: function() {
        var gangIndex = this._gangCards.length - 1;
        if (gangIndex < 0) {
            return;
        }

        var gangInfo = this._gangCards[gangIndex];
        var card = gangInfo.card;

        if (gangInfo.gang == MJ.Gang.BA) {
            this._pengCards.push(card);
            this._handCards.addCard(card, 1);
        } else if (gangInfo.gang == MJ.Gang.DIAN) {
            this._handCards.addCard(card, 3);
        } else {
            this._handCards.addCard(card, 4);
        }

        this._gangCards.splice(gangIndex, 1);

        this.resetPrevGang();
        return gangInfo;
    },

    /**
     * 被抢杠后取消最后一个杠牌
     * @param card
     */
    cancelLastGangForQG: function(card) {
        var gangIndex = this._gangCards.length - 1;
        if (gangIndex < 0) {
            return;
        }

        var gangInfo = this._gangCards[gangIndex];
        if (card) {
            if (gangInfo.card != card) {
                return;
            }
        } else {
            card = gangInfo.card;
        }

        this.getRoom().broadcastReplay("onCancelGangForQG", {
            playerIndex: this.getIndex(),
            gangInfo: gangInfo,
        });

        if (gangInfo.gang == MJ.Gang.BA) {
            this._pengCards.push(card);
            this._playCards.push({card: card, task: MJ.Task.HU});
        } else if (gangInfo.gang == MJ.Gang.DIAN) {
            this._handCards.addCard(card, 3);
        } else {
            this._handCards.addCard(card, 3);
            this._playCards.push({card: card, task: MJ.Task.HU});
        }

        this._gangCards.splice(gangIndex, 1);
        this.resetPrevGang();
        return gangInfo;
    },

    /**
     * 杠牌数量
     * @returns {Number}
     */
    getGangCount: function() {
        return this._gangCards.length;
    },

    getGangCards: function() {
        return this._gangCards;
    },

    getLastGang: function() {
        return (this._gangCards.length > 0) ? this._gangCards[0] : null;
    },

    /**
     * 设置最后一个杠不计分
     */
    setLastGangUnscored: function() {
        var gangInfo = this.getLastGang();
        if (gangInfo) {
            gangInfo.unscored = true;
        }
    },

    /**
     * 检查手牌是否能胡
     * @returns {Number} 牌型
     */
    checkHu: function() {
        if (!this.checkTingOrHuCondition()) {
            return MJ.Pattern.NONE;
        }

        var subConfig = this.getRoom().getSubConfig();
        var cards = this._handCards.getCards();

        return this.checkHuLogic(cards, this.getLastCard());
    },

    /**
     * 检查是否能胡某张牌
     */
    checkHuWithCard: function(card) {
        if (MJ.getColor(card) == this.getSelectColor()) {
            return MJ.Pattern.NONE;
        }

        if (!this.checkTingOrHuCondition()) {
            return MJ.Pattern.NONE;
        }

        var subConfig = this.getRoom().getSubConfig();
        var cards = clone(this._handCards.getCards());
        ExObject.addNumber(cards, card, 1);

        return this.checkHuLogic(cards, card);
    },

    /**
     * 本局胡的那的那牌
     * @returns {number|*}
     */
    getRoundHuCard: function() {
        return this._stat.huCard;
    },

    /**
     * 本局胡牌之后的手牌
     * @returns {{}}
     */
    getRoundHuCards: function() {
        if (!this.isRoundHu()) {
            return {};
        }

        var cards = clone(this.getHandCards());
        if (this.getRoundResult() == MJ.Result.DH) {
            ExObject.addNumber(cards, this.getRoundHuCard(), 1);
        }

        return cards;
    },

    checkTing: function() {
        var tings = [];

        if (!this.checkTingOrHuCondition()) {
            return tings;
        }

        var subConfig = this.getRoom().getSubConfig();
        var cards = this._handCards.getCards();

        subConfig.colors.forEach(function(color) {
            if (color == this.getSelectColor()) {
                return;
            }

            var cardNum = MJ.ColorCardNum[color];
            for (var p = 1, pm = cardNum.point; p <= pm; ++p) {
                var card = MJ.getValue(color, p);
                if (cards[card] === 4) {
                    continue;
                }

                var tmpCards = clone(cards);
                ExObject.addNumber(tmpCards, card, 1);

                var pattern = this.checkHuLogic(tmpCards, card);
                if (pattern != MJ.Pattern.NONE) {
                    tings.push({card: card, pattern: pattern});
                }
            }
        }.bind(this));

        return tings;
    },

    /**
     * 检查杠牌后听牌是否改变
     * 默认规则为：1、不能无听牌  2、不能产生新听牌
     * @param gangTask
     */
    checkTingChangeIfGang: function(gangTask) {
        this.gangCard(gangTask);
        var tings = this.checkTing();
        this.cancelLastGang();

        var oldTings = this.getTingCards();
        if (tings.length == 0 || tings.length > oldTings.length) {
            return true;
        }

        var change = false;
        for (var i = 0, il = tings.length; i < il; ++i) {
            var ting = tings[i];
            var exist = false;
            for (var io = 0, iol = oldTings.length; io < iol; ++io) {
                if (oldTings[io].card == ting.card) {
                    exist = true; break;
                }
            }

            if (!exist) {
                change = true;
            }
        }

        return change;
    },

    setBaoTing: function() {
        this._isBaoTing = true;
    },

    isBaoTing: function() {
        return this._isBaoTing;
    },

    isPassHu: function() {
        return this._isPassHu;
    },

    setPassHu: function(mark) {
        this._isPassHu = mark;
        if (mark) {
            this._passHuTimes++;
            if (this.isBaoTing()) {
                this._baoTingPassHuTimes++;
            }
        }
    },

    getPassHuTimes: function() {
        return this._passHuTimes;
    },

    getBaoTingPassHuTimes: function() {
        return this._baoTingPassHuTimes;
    },

    setTingCards: function(tings) {
        this._tingCards = tings;
    },

    getTingCards: function() {
        return this._tingCards;
    },

    /**
     * 某张牌出现过的张数
     * @param card
     */
    getAppearedCardNum: function(card) {
        var num = 0;

        this._playCards.forEach(function(cardInfo) {
            if (card == cardInfo.card && (!cardInfo.task || cardInfo.task == MJ.Task.HU)) {
                num += 1;
            }
        });

        for (var i = 0, l = this._pengCards.length; i < l; ++i) {
            if (card == this._pengCards[i]) {
                num += 3;
            }
        }

        for (var i = 0, l = this._gangCards.length; i < l; ++i) {
            if (card == this._gangCards[i].card) {
                num = 4; break;
            }
        }

        return num;
    },

    getHandCardNum: function(card) {
        return this._handCards.getCardNum(card);
    },

    getHandCards: function() {
        return this._handCards.getCards();
    },


    /******************************************************************
     * 统计与结算
     ******************************************************************/

    addRoundScoreDetail: function(reason, score) {
        if (ExObject.isObject(reason)) {
            this._stat.roundScoreDetails.push(reason);
        } else {
            this._stat.roundScoreDetails.push({reason:reason, score:score});
        }
    },

    setRoundResult: function(result, target, card) {
        this._stat.roundResult = result;
        if (target) {
            this._stat.roundTarget = target;
        }

        if (card && result > MJ.Result.DRAW) {
            this._stat.huCard = card;
        }
    },

    isRoundHu: function() {
        return this._stat.roundResult > MJ.Result.DRAW;
    },

    getRoundResult: function() {
        return this._stat.roundResult;
    },

    getRoundTarget: function() {
        return this._stat.roundTarget;
    },

    setRoundPattern: function(pattern, score) {
        this._stat.roundPattern = pattern;
        this._stat.roundPatternScore = score || 0;
    },

    getRoundPattern: function() {
        return this._stat.roundPattern;
    },

    isRoundTing: function() {
        return this._stat.roundPattern != MJ.Pattern.NONE;
    },

    getRoundPatternScore: function() {
        return this._stat.roundPatternScore;
    },

    addRoundGangScore: function(score) {
        logger.debug("addRoundGangScore: uid=%d, score=%d", this.uid(), score);
        this._stat.roundGangScore += score;
    },

    addRoundItemScores: function(item, score) {
        if (score) {
            ExObject.addNumber(this._stat.roundItemScores, item, score);
        }
    },

    unsetRoundItemScore: function(item) {
        delete this._stat.roundItemScores[item];
    },

    getRoundItemScore: function() {
        return ExObject.sumValue(this._stat.roundItemScores);
    },

    setRoundScoreRule: function(key) {
        this._stat.roundScoreRules[key] = 1;
    },

    unsetRoundScoreRule: function(key) {
        delete this._stat.roundScoreRules[key];
    },

    existRoundScoreRule: function(key) {
        return this._stat.roundScoreRules.hasOwnProperty(key);
    },

    getLastGangG: function() {
        return this._lastGang;
    },

    getLastGangGDeep: function() {
        if (this._lastGang) {
            var deep =  1;
            var a = this._lastGang;
            while(a.prevGang) {
                ++deep;
                a = a.prevGang;
            }
            return deep;
        }
        return 0;
    },

    resetLastGangG: function() {
        this._lastGang = null;
    },

    /******************************************************************
     * 重载的接口
     ******************************************************************/

    getInfo: function() {
        var info = this._super();
        return info;
    },

    getReconnectInfo: function(isSelf) {
        var info = this._super(isSelf);

        info.playCards = this.getPlayCards();
        info.pengCards = this._pengCards;
        info.gangCards = this._gangCards;
        info.chiCards = this._chiCards;
        info.pengCardsFrom = this._pengCardsFrom;
        info.isBaoTing = this.isBaoTing();
        info.cardNum = this._handCards.getCardNum();
        info.score = this._stat.score;
        info.huCard = this._stat.huCard;
        info.roundResult = this._stat.roundResult;

        var room = this.getRoom();

        info.lastCard = 0;
        if (room.isPlaying() && this.getIndex() == room.getCurPlay() && !room.getCurTask()) {
            info.lastCard = this._handCards.getLastNewCard();
        }

        if (isSelf) {
            info.cards = this._handCards.getCards();
            info.hangupTasks = room.getHangupTask().getUncommitted(this.getIndex()) || {};
            if (room.getCurPlay() == this.getIndex()) {
                if (room.getHangupTask().existOther(this.getIndex())) {
                    info.existOtherHangup = true;
                }
            }
        }

        if (room.getCurPlay() == this.getIndex()) {
            if (room.getCurTask() == MJ.Task.GANG) {
                info.lastGang = this.getLastGang();
            }
        }

        if (isSelf || this.getRoom().isSelectColorFinished()) {
            info.selectColor = this._selectColor;
        } else {
            info.selectColor = this.isSelectColor() ? 0 : -1;
        }

        return info;
    },

    getSettlementInfo: function() {
        var info = this._super();
        return info;
    },

    onStartNewRound: function() {
        this._super();

        this._selectColor = -1;

        this._stat.roundResult = MJ.Result.DRAW;
        this._stat.roundTarget = [];
        this._stat.roundPattern = MJ.Pattern.NONE;
        this._stat.roundPatternScore = 0;
        this._stat.roundGangScore = 0;
        this._stat.roundItemScores = {};
        this._stat.roundScoreRules = {};
        this._stat.roundScoreDetails = [];
        this._stat.huCard = 0;

        this._handCards.reset();
        this._initColors = [];

        this._playCards = [];
        this._pengCards = [];
        this._gangCards = [];
        this._chiCards = [];

        this._lastGang = null;

        this._pengCardsFrom = {};

        this._isBaoTing = false;
        this._isPassHu = false;
        this._passHuTimes = 0;
        this._baoTingPassHuTimes = 0;
        this._tingCards = [];
    },

    /******************************************************************
     * 需要重载的接口
     ******************************************************************/

    /**
     * 初始手牌需要的张数
     * @param playerIndex
     * @returns {number}
     */
    getInitHandCardNeedNum: function() {
        return (this.getIndex() != this.getRoom().getDealerIndex()) ? 13 : 14;
    },

    /**
     * 初始化手牌时默认牌
     * @returns {array}
     */
    getInitHandCardPreCards: function() {
        var controlValue = this.getUserValue("controlValue");
        if (controlValue) {
            var room = this.getRoom();
            var cards = [];

            if (controlValue >= 80) {
                var looks = room.getPublicCards().lookCards(3);
                cards.merge(looks);
                cards.merge(looks);
                cards.merge(looks);
            } else if (controlValue >= 60) {
                var looks = room.getPublicCards().lookCards(2);
                cards.merge(looks);
                cards.merge(looks);
                cards.merge(looks);
            } else if (controlValue >= 40) {
                var looks = room.getPublicCards().lookCards(3);
                cards.merge(looks);
                cards.merge(looks);
            } else if (controlValue >= 20) {
                var looks = room.getPublicCards().lookCards(2);
                cards.merge(looks);
                cards.merge(looks);
            } else if (controlValue >= 10) {
                var looks = room.getPublicCards().lookCards(1);
                cards.merge(looks);
                cards.merge(looks);
            }

            return cards;
        }
    },

    checkTingOrHuCondition: function() {
        // 定缺
        if (this.getRoom().isSelectColor()) {
            if (MJ.existColor(this._handCards.getCards(), this.getSelectColor())) {
                return false;
            }
        }

        return true;
    },

    checkChiCondition: function(card) {
        return true;
    },

    /**
     * 检查胡牌的逻辑
     * @param cards
     */
    checkHuLogic: function(cards, huCard) {
        var num4 = 0;
        var num3 = 0;
        var num2 = 0;
        var num = 0;

        ExObject.each(cards, function(c, n) {
            if (n == 2) {
                num2++;
            } else if (n == 3) {
                num3++;
            } else if (n == 4) {
                num4++;
            }
            num += n;
        });

        if (num == 2) { // 单调
            return (num2 == 1) ? MJ.Pattern.SINGLE : MJ.Pattern.NONE;
        }

        if ((num2 + num4*2) == 7) { // 七对
            return MJ.Pattern.PAIR7;
        }

        if (num2 == 1 && (num3*3 + 2) == num) { // 大对子
            return MJ.Pattern.PAIR;
        }

        var room = this.getRoom();
        var colors = room.getSubConfig().colors;

        var pattern = MJ.Pattern.NORMAL;
        for (var i = 0, l = colors.length; i < l; ++i) {
            var color = colors[i];
            var colorCards = MJ.getColorCards(cards, color);

            if (MJ.isBasicColor(color)) {
                if (!MJ.checkHuColorCards(MJ.getValue(color, 1), MJ.getValue(color, MJ.COLOR_CARD_NUM), colorCards)) {
                    pattern = MJ.Pattern.NONE; break;
                }
            } else {
                if (!MJ.checkPairHuColorCards(colorCards)) {
                    pattern = MJ.Pattern.NONE; break;
                }
            }
        }
        return pattern;
    },

    getColorsWithCards: function(cards) {
        var colors = {};

        ExObject.eachKeyNum(cards, function(card, num) {
            colors[MJ.getColor(card)] = 1;
        });

        this._pengCards.forEach(function (card) {
            colors[MJ.getColor(card)] = 1;
        });

        this._gangCards.forEach(function (gangInfo) {
            if (gangInfo.card) {
                colors[MJ.getColor(gangInfo.card)] = 1;
            }
        });

        this._chiCards.forEach(function(chiInfo) {
            colors[MJ.getColor(chiInfo.card)] = 1;
        });

        return ExObject.numberKeys(colors);
    },
});